//
//  Closure.swift
//  cnswift-demo
//
//  Created by YangWei on 2020/12/25.
//  Copyright © 2020 YangWei. All rights reserved.
//

import UIKit

// 虚拟类型
class Server: NSObject {
}
class Picture: NSObject {
}
class SomeView: NSObject {
    var currentPicture: Picture = Picture()
}
func download(_ url: String, from: Server) -> Picture? {
    return Picture()
//    return nil
}

var completionHandlers: [() -> Void] = []

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 } // 如果不加 self 会报❌
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

class SomeOtherClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { [self] in x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

/*
 self 是结构体或者枚举的实例，可以隐式地引用 self (不用写self)。
 当 self 是结构体或者枚举的实例时，逃逸闭包不能捕获可修改的 self 引用。
 结构体和枚举不允许共享可修改性
 */
struct SomeStruct {
    var x = 10
    mutating func doSomething() {
        /*
         someFunctionWithEscapingClosure 是一个逃逸闭包
         引用了 self.x 的引用
         x 是一个可变的值
         这样不允许
         */
//        someFunctionWithEscapingClosure { x = 100 } // Error
        someFunctionWithNonescapingClosure { x = 200 }  // Ok
    }
}

func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}

func serveV2(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}

var customerProviders: [() -> String] = []

/* 自动闭包允许逃逸，同时使用 @autoclosure 和 @escaping 标志 */
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
    customerProviders.append(customerProvider)
}

class Closures: SWDBaseObject {
    
    class func run() {
        
        let closures = Closures()
        
        // 1.1
        closures.theSortedMethod()
        
        // 1.2
        closures.closureExpressionSyntax()
        
        // 1.3
        closures.inferringTypeFromContext()
        
        // 1.4
        closures.implicitReturnsFromSingleExpressionClosures()
        
        // 1.5
        closures.shorthandArgumentNames()
        
        // 1.6
        closures.operatorMethods()
        
        // 2
        closures.trailingClosures()
        
        // 3
        closures.capturingValues()
        
        // 4
        closures.closuresAreReferenceTypes()
        
        // 5
        closures.escapingClosures()
        
        // 6
        closures.autoclosures()
    }
    
    /// 1.1 Sorted 方法
    func theSortedMethod() {
        let title = "1.1 Sorted 方法";
        printStartLine(title)
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]

        /* 内嵌函数是一个有名字且能从其上层函数捕获值的闭包 */
        func backward(_ s1: String, _ s2: String) -> Bool {
            // 倒序排列
            return s1 > s2
        }
        
        let reversedNames = names.sorted(by: backward)
        print(reversedNames)
        
        printEndLine(title)
    }
    
    /// 1.2 闭包表达式语法
    func closureExpressionSyntax() {
        let title = "1.2 闭包表达式语法";
        printStartLine(title)
        
        /* 一般形式
         { (parameters) -> (return type) in
             statements
         }
         */
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]
        // 将之前的内嵌函数传值换成闭包传值
        let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
        print(reversedNames)
        
        printEndLine(title)
    }
    
    /// 1.3 从语境中推断类型
    func inferringTypeFromContext() {
        let title = "1.3 从语境中推断类型";
        printStartLine(title)
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]
        // 将之前闭包传值中字符串类型省略
        let reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
        print(reversedNames)
        
        printEndLine(title)
    }
    
    /// 1.4 从单表达式闭包隐式返回
    func implicitReturnsFromSingleExpressionClosures() {
        let title = "1.4 从单表达式闭包隐式返回";
        printStartLine(title)
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]
        // 将之前的单表达式闭包，省略 return
        let reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
        print(reversedNames)
        
        printEndLine(title)
    }
    
    /// 1.5 简写的实际参数名
    func shorthandArgumentNames() {
        let title = "1.5 简写的实际参数名";
        printStartLine(title)
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]
        // 将之前的单表达式闭包进一步简化，简写实参
        let reversedNames = names.sorted(by: { $0 > $1 } )
        print(reversedNames)
        
        printEndLine(title)
    }
    
    /// 1.6 运算符函数
    func operatorMethods() {
        let title = "1.6 运算符函数";
        printStartLine(title)
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]
        // 将之前的单表达式闭包进一步简化，使用运算符函数
        let reversedNames = names.sorted(by: >)
        print(reversedNames)
        
        
        printEndLine(title)
    }
    
    /// 函数定义
    func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
        if let picture = download("photo.jpg", from: server) {
            completion(picture)
        } else {
            onFailure()
        }
    }
    
    /// 2 尾随闭包
    func trailingClosures() {
        let title = "2 尾随闭包";
        printStartLine(title)
        
        let names = ["Chris","Alex","Ewa","Barry","Daniella"]
        // 使用尾随闭包书写
        var reversedNames = names.sorted() { $0 > $1 }
        print(reversedNames)
        
        /* 闭包表达式作为函数的唯一实际参数传入，可以进一步省略函数调用的括号 */
        reversedNames = names.sorted { $0 > $1 }
        print(reversedNames)
        
        /*
         使用带有尾随闭包的 map(_:) 方法将包含 Int 值的数组转换成包含 String 值的数组。
         数组  [16,58,510] 被转换成新的数组 ["OneSix","FiveEight","FiveOneZero"] ：
         */
        let digitNames = [
            0: "Zero",1: "One",2: "Two",  3: "Three",4: "Four",
            5: "Five",6: "Six",7: "Seven",8: "Eight",9: "Nine"
        ]
        
        let numbers = [16,58,510]
        
        let strings = numbers.map { (number) -> String in
            var number = number
            var output = ""
            repeat {
                output = digitNames[number % 10]! + output
                number /= 10
            } while number > 0
            return output
        }
        print(strings)
        
        /*
         函数接收多个闭包，可省略第一个尾随闭包的实参标签，后续的尾随闭包标签不能省略。
         比如说，下面的函数给照片墙加载图片
         */
        let someServer = Server()
        let someView = SomeView()
        loadPicture(from: someServer) { picture in
            someView.currentPicture = picture
        } onFailure: {
            print("Couldn't download the next picture.")
        }
        
        printEndLine(title)
    }
    
    /// 生成一个增量器（闭包）
    func makeIncrementer(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementer() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementer
    }
    
    /// 3 捕获值
    func capturingValues() {
        let title = "3 捕获值";
        printStartLine(title)
        
        let incrementByTen = makeIncrementer(forIncrement: 10)

        let r1 = incrementByTen() // 10
        let r2 = incrementByTen() // 20
        let r3 = incrementByTen() // 30
        
        let incrementBySeven = makeIncrementer(forIncrement: 7)
        let r4 = incrementBySeven() // 7
        
        let r5 = incrementByTen() // 40
        
        print(r1, r2, r3, r4, r5)
        
        printEndLine(title)
    }
    
    /// 4 闭包是引用类型
    func closuresAreReferenceTypes() {
        let title = "4 闭包是引用类型";
        printStartLine(title)
        
        let incrementByTen = makeIncrementer(forIncrement: 10)

        let r1 = incrementByTen() // 10
        let r2 = incrementByTen() // 20
        let r3 = incrementByTen() // 30
        let r4 = incrementByTen() // 40
        
        // incrementByTen 和 alsoIncrementByTen 指向同一个闭包
        let alsoIncrementByTen = incrementByTen
        let r5 = alsoIncrementByTen() // 50
        print(r1, r2, r3, r4, r5)
        
        printEndLine(title)
    }
    
    /// 5 逃逸闭包
    func escapingClosures() {
        let title = "5 逃逸闭包";
        printStartLine(title)
        
        let instance = SomeClass()
        instance.doSomething()
        // 先执行的是非逃逸闭包
        print(instance.x) // 200
         
        completionHandlers.first?()
        // 执行逃逸闭包
        print(instance.x) // 100
        
        /* 通过把 self 放在闭包捕获列表来捕获 self 的 doSomething() */
        let otherInstance = SomeOtherClass()
        otherInstance.doSomething()
        print(otherInstance.x) // 200
         
        completionHandlers[1]()
        // 执行逃逸闭包
        print(otherInstance.x) // 100
        
        /* SomeStruct */
        var someStruct = SomeStruct()
        someStruct.doSomething()
        print(someStruct.x)
        
        printEndLine(title)
    }
    
    /// 6 自动闭包
    func autoclosures() {
        let title = "6 自动闭包";
        printStartLine(title)
        
        /* 闭包如何延迟求值 */
        var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
        print(customersInLine.count) // 5
         
        let customerProvider = { customersInLine.remove(at: 0) }
        print(customersInLine.count) // 5
        
        print("Now serving \(customerProvider())!") // "Now serving Chris!"
        print(customersInLine.count) // 4
        
        /* 当传一个闭包作为实际参数到函数的时候，会得到与延迟处理相同的行为 */
        // customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
        serve(customer: { customersInLine.remove(at: 0) } )
        // "Now serving Alex!"
        print(customersInLine.count) // 3
        
        // customersInLine is ["Ewa", "Barry", "Daniella"]
        serveV2(customer: customersInLine.remove(at: 0))
        // "Now serving Ewa!"
        print(customersInLine.count) // 2
        
        /* 自动闭包允许逃逸，同时使用 @autoclosure 和 @escaping 标志 */
        collectCustomerProviders(customersInLine.remove(at: 0))
        collectCustomerProviders(customersInLine.remove(at: 0))
         
        print("Collected \(customerProviders.count) closures.")
        // "Collected 2 closures."
        for customerProvider in customerProviders {
            print("Now serving \(customerProvider())!")
        }
        // "Now serving Barry!"
        // "Now serving Daniella!"
        
        
        printEndLine(title)
    }
}


